WannaDie 勒索病毒 详细分析
0x01 程序信息
病毒名:lantern installer.exe
Size:4,272,640 字节
壳:UPX 0.89.6 - 1.02 / 1.05 - 2.90
编写语言:易语言
[MD5] 7C72C7DADDF61B98804CBA0C5EDA4669
[SHA256] F17E7BBFC5B003672C872BD5B4FE2BD4B15CAA1E91045FE40D2FA2DFBA607837
0x02 程序行为
(1) 隐藏自身
(2) 释放病毒子体
(3) 关闭UAC
(4) 注册开机启动项
(5) 进程检测
(6) 删除卷影副本
(7) 发送邮件
0x03 分析思路
(1) 查看是否加壳.
(2) 观察病毒运行起来之后做了一些什么.
(3) 用火绒剑载入运行, 检测一些敏感行为.
0x04 初始工作
(1) PEID载入,发现了UPX 壳, 用ESP定律轻松脱掉。
(2) 根据一下特征分析得出这是一个易语言程序。
易语言的标准图标
易语言的跳转表
(3) 在虚拟机中运行程序,取得一些程序特征:
1) 自我隐藏
2) 打不开任务管理器
3) 桌面背景被换
4) 各个盘根目录都有勒索图片
5) 文件被加密后大小不变 <最重要的特征, 以此可以判断使用了流加密算法>
6) 写了开机启动项
(4) 运行截图
(5) 拿到一些基础的特征, 就可以开始逆向分析了.
0x05 病毒母体分析
(1) 病毒母体最主要的功能就是把病毒子体释放到系统临时目录,并执行。
(2) 其余的功能就是病毒子体的前5步一样了,为了篇幅不太臃肿,就省略了。
0x06 病毒子体分析
(1) 通过易语言的特征码:FF 55 FC 5F 5E 89 5D F4 下断点,
这里会处理main函数, 按钮事件, 时钟 … 等各种事件。
(2) 下好断点之后,运行,就会断到此处,按F7即可找到main函数,继续往下分析即可。
(3) 创建Event,用来防多开。
(4) 提升进程权限
(5) 提升自身进程优先级
6) 写注册表破坏UAC:
HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\System\EnableLUA
写入值:<0>
(7) 修改开机启动项
HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Run\WannaDie
写入值:“<C:\Users\AYZRxx\AppData\Local\Temp\dump_daughter_.exe”>
(8) 禁用注册表工具, 写注册表
HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Policies\System\Disableregistrytools
写入值:<1>
(9) 禁用任务管理器, 写注册表
HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Policies\System\DisableTaskMgr
写入值: <1>
(10) 设置自身文件属性为<隐藏|系统|只读|存档>
(11) 删除卷影副本, 执行CMD命令: "cmd /c vssadmin delete shadow /all /quiet & wmic shadowcopy delete & bcdedit /set {default} boostatu"
(12) 生成随机序列号,这些序列号只是用来辨别中毒机器的,并不是真正的解密Key。
(13) 获取盘符
(14)
1)这个勒索病毒的加密方式很渣,每一种后缀都需要全盘遍历一次, 在进行加密, 这样会导致加密效率非常低。
2) 这是所有会加密的后缀,但是有些后缀只会修改名称,并不会加密数据
“*.zip,*.rar,*.7z,*.e,*.ec,*.bak,*.cpp,*.c,*.h,*.cs,*.dOc,*.docx,*.xls,*.xlsx,*.ppt,*.pptx,*.mdb,*.php,*.gif,*.rtf,*.png,*.jpg,*.jpeg,*.txt,*.avi,*.msg,*.mp3
*.mp4,*.pdf,*.pps”
(15) 加密完成后, 将勒索图片写到桌面。
(16) 遍历所有盘符,在盘符根目录写勒索文件。
(17) 设置桌面背景为勒索图片。
(18) 载入勒索窗口
(19) 病毒会通过访问www.baidu.com来判断是否联网
网络没有连接则弹出此框
(20) 连接邮箱
(21) 拍摄快照
(22) 把拍摄的快照当作附件,加入邮件,并发送到指定邮箱。
(23) 删除快照文件
0x07 分析解密部分
(1) 加了一个反调试函数,不过只是简单的检测调试端口,被OD插件给过了。
(2) 比较密钥是否正确,因为需要继续分析,所以手动修改下,使之条件成立。
(3) 调用解密窗口,并且创建一个解密线程
(4) 解密线程的无非就是遍历磁盘来解密文件。
0x08 核心加密代码分析
(1) 计算加密Key,获取物理驱动器0的序列号<不确定> 0xB3211363
(2) 将这个0xB3211363十六进制的数转换为10进制的字符串, 就得到了最终用来加密的Key。
(3) 遍历*.ec文件并加密。
(4) 先读入文件
(5) 生成0x100大小的密码表
(6) 加密函数,这个函数就需要用到IDA分析了。
(7) 对这个函数F5之后会修改一下参数类型就可以直接复制到VS中使用了,代码如下:
说个IDA F5复制代码的小技巧:尽量使用BYTE类型, 而不是char类型, 因为char类型为数组的下标时,会被当成4个字节,其余3个字节是未知的,有可能导致错误。
# include <stdio.h>
# include <string.h>
#include <atlstr.h>
#include <windows.h>
typedef unsigned int uint;
uint POLYNOMIAL = 0xEDB88320;
int have_table = 0;
uint table[256];
void make_table()
{
int i = 0, j = 0, crc = 0;
have_table = 1;
for (i = 0; i < 256; i++)
for (j = 0, table[i] = i; j < 8; j++)
table[i] = (table[i] >> 1) ^ ((table[i] & 1) ? POLYNOMIAL : 0);
}
uint crc32(uint crc, char *buff, int len)
{
if (!have_table) make_table();
crc = ~crc;
for (int i = 0; i < len; i++)
crc = (crc >> 8) ^ table[(crc ^ buff[i]) & 0xff];
return ~crc;
}
DWORD GetDriveFeature()
{
HANDLE hPhysicalDriveIOCTL;
DWORD dwCbBytesReturned;
char driveName[20] = "\\\\.\\PhysicalDrive0";
hPhysicalDriveIOCTL = CreateFileA(driveName, 0, 1 | 2, 0, 3, 0, 0);
if ((DWORD)hPhysicalDriveIOCTL == -1)
{
return FALSE;
}
DWORD dwBufferSize;
dwBufferSize = 1024;
char query[12] = {};
char buffer[1024] = {};
BOOL bSt;
DWORD dwCrc1;
bSt = DeviceIoControl(hPhysicalDriveIOCTL, 2954240, query,
12, buffer, dwBufferSize, &dwCbBytesReturned, 0);
if (bSt = TRUE)
{
dwCrc1 = crc32(0, buffer, 1024);
}
CloseHandle(hPhysicalDriveIOCTL);
return dwCrc1;
}
//交换字节
BYTE *__cdecl ChangeByte(BYTE *a1, BYTE *a2)
{
BYTE *result; // eax@1
char v3; // cl@1
result = a1;
v3 = *a1;
*a1 = *a2;
*a2 = v3;
return result;
}
//生成密码表(密钥, 密钥长度, 生成0x100尺寸的密码表)
int GenerateKeyTable(char* szKey, signed int nKeyLen, BYTE* szKeyTable)
{
signed int v3 = 0; // eax@1
BYTE *v4 = 0; // ebp@1
int result = 0; // eax@3
BYTE v6 = 0; // esi@4
BYTE *v7 = 0; // ebx@4
bool v8 = 0; // zf@5
signed int v9 = 0; // [sp+4h] [bp-8h]@4
unsigned __int8 v10 = 0; // [sp+18h] [bp+Ch]@4
v3 = 0;
v4 = (BYTE *)szKeyTable;
do
{
*(BYTE *)(v3 + szKeyTable) = v3;
++v3;
} while (v3 < 0x100);
result = nKeyLen;
*(BYTE *)(szKeyTable + 0x100) = 0;
*(BYTE *)(szKeyTable + 0x101) = 0;
if (nKeyLen > 0)
{
v10 = 0;
//LOBYTE(v6) = 0;
v7 = v4;
v9 = 256;
do
{
v6 = (unsigned __int8)(*(BYTE *)(v10 + szKey) + v6 + *v7);
ChangeByte(v7++, &v4[v6]);
result = v9 - 1;
v8 = v9-- == 1;
v10 = (v10 + 1) % nKeyLen;
} while (!v8);
}
return result;
}
//RC4加密(被加密数据, 被加密数据长度, 0x100尺寸的密码表)
char __cdecl Rc4_Enc(BYTE *pbByEncData, int nLen, BYTE *pbKeyTable)
{
BYTE *v3; // esi@1
int v4; // ebx@1
BYTE result; // al@1
BYTE v6; // cl@1
BYTE v7; // di@2
BYTE v8; // bp@2
BYTE *v9; // ST20_4@3
BYTE *v10; // ST1C_4@3
BYTE v11; // [sp+8h] [bp-Ch]@3
BYTE v12; // [sp+20h] [bp+Ch]@1
BYTE v13; // [sp+20h] [bp+Ch]@3
v3 = pbKeyTable;
v4 = 0;
result = pbKeyTable[0x100];
v6 = pbKeyTable[0x101];
v12 = pbKeyTable[0x100];
if (nLen <= 0)
{
v3[0x100] = result;
v3[0x101] = v6;
}
else
{
v7 = v12;
v8 = v6;
do
{
v13 = v7 + 1;
v7 = v13;
v9 = &v3[(unsigned __int8)v13];
v11 = v8 + *v9;
v8 = v11;
v10 = &v3[(unsigned __int8)v11];
ChangeByte(v9, v10);
//因为这里只是进行异或, 经过测试这段代码可以加密, 也可以解密
pbByEncData[v4] ^= v3[((unsigned __int8)*v9 + (unsigned __int8)*v10) & 0xFF];
++v4;
} while (v4 < nLen);
result = v11;
v3[0x100] = v13;
v3[0x101] = v11;
}
return result;
}
int main()
{
//计算当前电脑的密钥, 测试机器的密钥为 "-1289677981"
// DWORD dwFeature = GetDriveFeature();
// CStringA csFeature;
// csFeature.Format("%d", dwFeature);
// printf("%s\n", csFeature.GetBuffer());
// csFeature.ReleaseBuffer();
HANDLE hFile = CreateFileA("C:\\Users\\AYZRxx\\Desktop\\11.ec",
GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ,
NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
DWORD dwFileSize = GetFileSize(hFile, NULL);
byte* pbFileBuf = new byte[dwFileSize];
DWORD dwRead = 0;
ReadFile(hFile, pbFileBuf, dwFileSize, &dwRead, NULL);
char szkey[0x10] = "-1289677981";
BYTE pbKeyTable[0x200] = {};
//生成0x100大小的密钥
GenerateKeyTable(szkey, 0xB, pbKeyTable);
//解密数据
Rc4_Enc(pbFileBuf, dwFileSize, pbKeyTable);
SetFilePointer(hFile, NULL, NULL, NULL);
WriteFile(hFile, pbFileBuf, dwFileSize, &dwRead, NULL);
CloseHandle(hFile);
system("pause");
return 0;
}
0x09 进程检测函数分析
(1) 时钟只是简单的结束任务管理器进程而已, 通过API 条件断点则可以搞定。
(2) API<DispatchMessage> 下条件断点 short ptr [[esp+4]+4] == WM_TIMER
(3) Timer结束进程: "taskmgr.exe" "任务管理器.exe"
Timer回调函数地址: 0040D2C6 时钟周期: 1000ms
udd文件及密码样本, 有需要的就找我把, 附件传不上来, 不知道为什么.
本文由看雪论坛 暗夜之刃 原创
转载请注明来自看雪社区
热门阅读
点击阅读原文/read,
更多干货等着你~